#include "InternalBallisticsSimulator.h"

#include <yaml-cpp/yaml.h>
void InternalBallisticsSimulator::initializeSpring() {
    std::cout << "初始化弹簧相关参数。" << std::endl;
    // std::cout << "初始化弹簧相关参数，请以米(M)作为输入单位"<<std::endl;
    std::cout << "输入弹簧长度(mm): ";
    std::cin >> m_springLength;
    m_springLength *= 0.001;
    std::cout << "输入弹簧外径(mm): ";
    std::cin >> m_springDiameter;
    m_springDiameter *= 0.001;
    std::cout << "输入弹簧丝粗细(mm): ";
    std::cin >> m_springThickness;
    m_springThickness *= 0.001;
    std::cout << "输入弹簧预压量(拍头完全释放时弹簧长度与弹簧原长差值)("
                 "如果弹簧够不到完全释放的拍头，输入负的差值)(mm): ";
    std::cin >> m_springPreloadLength;
    m_springPreloadLength *= 0.001;
    std::cout << "输入弹簧有效圈数: ";
    std::cin >> m_springEffectiveCoils;
}

void InternalBallisticsSimulator::initializeCylinder() {
    std::cout << "初始化气缸相关参数。" << std::endl;
    std::cout << "输入气缸长度(这里与拍头的有效行程同意)(mm): ";
    std::cin >> m_cylinderLength;
    m_cylinderLength *= 0.001;
    std::cout << "输入气缸内直径(mm): ";
    std::cin >> m_cylinderDiameter;
    m_cylinderDiameter *= 0.001;
    m_cylinderArea = 3.14159 * (m_cylinderDiameter / 2) * (m_cylinderDiameter / 2);
    m_cylinderVolume = m_cylinderArea * m_cylinderLength;
}

void InternalBallisticsSimulator::initializePistonShell() {
    std::cout << "初始化其他参数。" << std::endl;
    std::cout << "输入拍头质量(g): ";
    std::cin >> m_pistonMass;
    m_pistonMass *= 0.001;
    std::cout << "输入弹头质量(g): ";
    std::cin >> m_shellMass;
    m_shellMass *= 0.001;
    std::cout << "输入枪管长度(mm): ";
    std::cin >> m_barrelLength;
    m_barrelLength *= 0.001;
}

void InternalBallisticsSimulator::initializeOthers() {
    std::cout << "输入枪管口径(默认13)(mm): ";
    std::cin >> m_caliber;
    m_caliber *= 0.001;
    m_shellarea = 3.14159 * (m_caliber / 2) * (m_caliber / 2);
}

void InternalBallisticsSimulator::computeSpring() {
    std::cout << "开始计算弹簧参数(剪切弹性模量取7800N/mm^2)" << std::endl;
    double G = 7800.0;
    double d = m_springThickness * 1000;
    double n = m_springEffectiveCoils;  //弹簧有效圈数;
    double D = (m_springDiameter - m_springThickness) * 1000;

    m_springStiffness = (G * (d * d * d * d)) / (8.0 * n * (D * D * D)) * 9.8 * 1000;  // kgf/mm转化为N/m
    double maxforce = m_springStiffness * (m_cylinderLength + m_springPreloadLength);
    double minforce = m_springStiffness * (m_springPreloadLength);
    m_springEnergy = 0.5 * (maxforce + minforce) * m_cylinderLength;  //弹簧储能
    if (minforce < 0) {                                                      //短弹簧的特殊情况
        minforce = 0;
        m_springEnergy = 0.5 * (maxforce + minforce) * (m_cylinderLength + m_springPreloadLength);
        std::cout << "短弹簧！" << std::endl;
    }

    std::cout << "弹簧劲度系数(N/m):" << m_springStiffness << std::endl;
    std::cout << "最大压力(N):" << maxforce << std::endl;
    std::cout << "最小压力(N):" << minforce << std::endl;
    std::cout << "弹簧储能(J):" << m_springEnergy << std::endl;
}

void InternalBallisticsSimulator::initialize() {
    initializeSpring();
    initializeCylinder();
    initializePistonShell();
    while (1) {
        char input;
        std::cout << "是否自定义口径？(Y/N): ";
        std::cin >> input;
        if (input == 'Y' || input == 'y') {
            initializeOthers();
            break;
        } else if (input == 'N' || input == 'n') {
            break;
        }
    }
    computeSpring();
    std::cout << "参数输入完成" << std::endl << std::endl;
}

void InternalBallisticsSimulator::initializefromyaml(const std::string& Filepath) {
    YAML::Node config = YAML::LoadFile(Filepath);
    float cylinderLength = config["cylinderLength"].as<float>();
    float cylinderEffectiveLength = config["cylinderEffectiveLength"].as<float>();
    float cylinderInsideDiameter = config["cylinderInsideDiameter"].as<float>();
    float springLength = config["springLength"].as<float>();
    float springOutsideDiameter = config["springOutsideDiameter"].as<float>();
    float springThickness = config["springThickness"].as<float>();
    float springPreloadLength = config["springPreloadLength"].as<float>();
    float springEffectiveCoils = config["springEffectiveCoils"].as<float>();
    float pistonMass = config["pistonMass"].as<float>();
    float barrelLength = config["barrelLength"].as<float>();
    float barrelCaliber = config["barrelCaliber"].as<float>();
    float bulletMass = config["bulletMass"].as<float>();

    initializeall(cylinderLength, cylinderEffectiveLength, cylinderInsideDiameter, springLength,
                  springOutsideDiameter, springThickness, springPreloadLength, springEffectiveCoils,
                  pistonMass, barrelLength, barrelCaliber, bulletMass);
    computeSpring();
    std::cout << "参数输入完成" << std::endl << std::endl;
}

void InternalBallisticsSimulator::initializeall(float cylinderLength, float cylinderEffectiveLength,
                                                float cylinderInsideDiameter, float springLength,
                                                float springOutsideDiameter, float springThickness,
                                                float springPreloadLength, float springEffectiveCoils,
                                                float pistonMass, float barrelLength, float barrelCaliber,
                                                float bulletMass) {
    m_cylinderLength = 0.001 * static_cast<double>(cylinderLength);
    m_cylinderEffectiveLength = 0.001 * static_cast<double>(cylinderEffectiveLength);
    m_cylinderDiameter = 0.001 * static_cast<double>(cylinderInsideDiameter);
    m_springLength = 0.001 * static_cast<double>(springLength);
    m_springDiameter = 0.001 * static_cast<double>(springOutsideDiameter);
    m_springThickness = 0.001 * static_cast<double>(springThickness);
    m_springPreloadLength = 0.001 * static_cast<double>(springPreloadLength);
    m_springEffectiveCoils = static_cast<double>(springEffectiveCoils);
    m_pistonMass = 0.001 * static_cast<double>(pistonMass);
    m_barrelLength = 0.001 * static_cast<double>(barrelLength);
    m_caliber = 0.001 * static_cast<double>(barrelCaliber);
    m_shellMass = 0.001 * static_cast<double>(bulletMass);

    m_cylinderArea = 3.14159 * (m_cylinderDiameter / 2) * (m_cylinderDiameter / 2);
    m_cylinderVolume = m_cylinderArea * m_cylinderEffectiveLength;
    m_shellarea = 3.14159 * (m_caliber / 2) * (m_caliber / 2);
}

void InternalBallisticsSimulator::stateinitialize() {
    m_pistonPosition = m_cylinderLength;  //拍头位置，0为完全释放（紧贴推嘴）
    m_pistonVelocity = 0;                 //拍头速度
    m_pistonAcceleration = 0;             //拍头加速度
    m_pistonForce = 0;                    //拍头受力
    m_pistonDoneFlag = 0;                 //拍头触底标志

    m_shellPosition = 0;      //弹头位置，0为位于枪管底部（紧贴推嘴）
    m_shellVelocity = 0;      //弹头速度
    m_shellAcceleration = 0;  //弹头加速度
    m_shellForce = 0;         //弹头受力

    m_airVolume = m_cylinderVolume;         //当前气体体积
    m_airPressure = m_atmosphericPressure;  //当前气体气压
}

void InternalBallisticsSimulator::computePressure() {
    double cylinderVolume = m_cylinderVolume * (m_pistonPosition / m_cylinderLength);
    double barrelVolume = (3.14159 * (m_caliber / 2) * (m_caliber / 2) * m_shellPosition);  //单位为立方米
    m_airVolume = cylinderVolume + barrelVolume;
    if (m_pistonPosition > m_cylinderEffectiveLength) {
        m_airPressure = m_atmosphericPressure;
    } else {
        m_airPressure =
            m_atmosphericPressure * pow((m_cylinderVolume / m_airVolume), 1.4);  //双原子气体绝热膨胀
    }
}

void InternalBallisticsSimulator::computeForces() {
    m_airPressureDiff = m_airPressure - m_atmosphericPressure;  //内外压强差

    double airForcePiston = m_airPressureDiff * m_cylinderArea;  //气体对拍头的压差阻力
    double airForceShell = m_airPressureDiff * m_shellarea;      //气体对弹体的压差推力

    double springForcePiston =
        m_springStiffness * (m_pistonPosition + m_springPreloadLength);  //弹簧对拍头推力
    if (springForcePiston < 0) {                                         //短弹簧的特殊情况
        springForcePiston = 0;
    }

    m_pistonForce = springForcePiston - airForcePiston;  //计算合力
    if (m_pistonVelocity > 0) {                          //摩擦阻力修正
        m_pistonForce -= m_pistonDragforce;
    } else {
        m_pistonForce += m_pistonDragforce;
    }
    m_shellForce = airForceShell;
    if (m_pistonVelocity > 0) {  //摩擦阻力修正
        m_pistonForce -= m_shellDragforce;
    } else {
        m_pistonForce += m_shellDragforce;
    }
}

void InternalBallisticsSimulator::computeAcceleration() {
    m_pistonAcceleration = m_pistonForce / m_pistonMass;
    m_shellAcceleration = m_shellForce / m_shellMass;
}
void InternalBallisticsSimulator::computeVelocity() {
    m_pistonVelocity += m_pistonAcceleration * m_timeStep;
    m_shellVelocity += m_shellAcceleration * m_timeStep;
}
void InternalBallisticsSimulator::computePosition() {
    m_pistonPosition -= m_pistonVelocity * m_timeStep;
    if (m_pistonPosition < 0 && m_pistonDoneFlag == 0) {
        std::cout << "拍头触底！" << std::endl;  //很少发生
        //m_pistonDoneFlag = 1;
        m_pistonPosition = 0;
        m_pistonVelocity = 0;
    }
    m_shellPosition += m_shellVelocity * m_timeStep;
}
std::string InternalBallisticsSimulator::outputData(int step) {
    double time_ms = m_timeStep * step * 1000;
    double volumeratio = m_airVolume / m_cylinderVolume;
    double pressureratio = m_airPressure / m_atmosphericPressure;
#if 0  //完整数据展示
    std::cout << "时间(ms):" << time_ms << ":   弹头位置: " << m_shellPosition << ", 弹头速度: " << m_shellVelocity
              << ", 弹头加速度: " << m_shellAcceleration;
    std::cout << "              拍头位置: " << m_pistonPosition << ", 拍头速度: " << m_pistonVelocity
              << ", 拍头加速度: " << m_pistonAcceleration;
    std::cout << "              当前气体体积/气缸体积: " << volumeratio << ", 当前气压/大气压： " << pressureratio
              << std::endl
              << std::endl;
#else
    std::cout << "时间(ms):" << std::fixed << std::setprecision(1) << std::setfill('0') << std::setw(4)
              << time_ms;
    std::cout << ":   弹头位置: " << std::fixed << std::setprecision(4) << m_shellPosition
              << ", 弹头速度: " << std::fixed << std::setprecision(3) << std::setfill('0') << std::setw(6)
              << m_shellVelocity << ", 膛内气压/大气压： " << std::fixed << std::setprecision(3)
              << pressureratio;
    std::cout << ",         拍头位置: " << std::fixed << std::setprecision(3) << m_pistonPosition
              << ", 拍头速度: " << std::fixed << std::setprecision(3) << m_pistonVelocity
              << ", 拍头加速度: " << std::fixed << std::setprecision(3) << m_pistonAcceleration << std::endl;
#endif
    std::ostringstream oss;  // 使用ostringstream来构建字符串

    oss << std::fixed;

    oss << "time_ms:" << std::setprecision(1) << std::setfill('0') << std::setw(4) << time_ms;
    oss << " shellPosition:" << std::setprecision(4) << m_shellPosition;
    oss << " shellVelocity:" << std::setprecision(3) << std::setfill('0') << std::setw(6) << m_shellVelocity;
    oss << " pressureratio:" << std::setprecision(3) << pressureratio;
    oss << " pistonPosition:" << std::setprecision(3) << m_pistonPosition;
    oss << " pistonVelocity:" << std::setprecision(3) << m_pistonVelocity;
    oss << " pistonAcceleration:" << std::setprecision(3) << m_pistonAcceleration;
    oss << std::endl;

    // 返回oss中的字符串表示
    return oss.str();
}

std::string InternalBallisticsSimulator::computeEfficiency() {
    double shellEK = 0.5 * m_shellMass * m_shellVelocity * m_shellVelocity;  //弹头动能
    double shellEK2 = shellEK / ((m_caliber * 100) * (m_caliber * 100));     //枪口比动能
    double efficiency = shellEK / m_springEnergy;                            //能量利用率

    std::cout << "弹头动能(焦): " << shellEK << ", 枪口比动能: " << shellEK2
              << ", 弹簧储能: " << m_springEnergy << std::endl
              << "——————————能量利用率——————————: " << efficiency << std::endl
              << std::endl;

    std::ostringstream oss;  // 使用ostringstream来构建字符串

    oss << std::fixed;

    oss << "shellEK:" << std::setprecision(3) << shellEK;
    oss << " EKratio:" << std::setprecision(3) << shellEK2;
    oss << " springEnergy:" << std::setprecision(3) << m_springEnergy;
    oss << " efficiency:" << std::setprecision(3) << efficiency;

    oss << std::endl;

    // 返回oss中的字符串表示
    return oss.str();
}
std::string InternalBallisticsSimulator::runSim() {
    stateinitialize();

    std::filesystem::create_directories("../data");
    // 获取当前时间并格式化为字符串
    auto now = std::chrono::system_clock::now();
    std::time_t nowTime = std::chrono::system_clock::to_time_t(now);
    std::tm* localTime = std::localtime(&nowTime);
    std::ostringstream filename;
    filename << "../data/" << std::put_time(localTime, "%Y-%m-%d_%H:%M-%S") << ".csv";
    // 在 "data" 目录下创建 CSV 文件
    std::ofstream file;
    file.open(filename.str(), std::ios_base::out);
    if (file.is_open()) {
        std::cout << "File created: " << filename.str() << std::endl;
        // clang-format off
        file << std::setw(16) << "time(step)"
             << "," << std::setw(16) << "shellAcceleration"
             << "," << std::setw(16) << "shellPosition"
             << "," << std::setw(16) << "shellVelocity"
             << "," << std::setw(16) << "pistonPosition"
             << "," << std::setw(16) << "efficiency"
             << ",";
        file << std::setw(16) << std::endl;
        // clang-format on
        file.flush();
    } else {
        std::cerr << "Failed to create file: " << filename.str() << std::endl;
    }

    int step = 0;
    std::string result;
    for (step = 0; ((step < 20000) && (m_shellPosition < m_barrelLength)); step++) {
        computePressure();
        computeForces();
        computeAcceleration();
        computeVelocity();
        computePosition();
        if (step % m_outputInterval == 0) {
            result += outputData(step);
        }
        double shellEK = 0.5 * m_shellMass * m_shellVelocity * m_shellVelocity;  //弹头动能
        double efficiency = shellEK / m_springEnergy;                            //能量利用率
        // clang-format off
        file << std::setw(16) << std::fixed << std::setprecision(4) << step << ","
             << std::setw(16) << std::fixed << std::setprecision(4) << m_shellAcceleration << ","
             << std::setw(16) << std::fixed << std::setprecision(4) << m_shellPosition << "," 
             << std::setw(16)<< std::fixed << std::setprecision(4) << m_shellVelocity << "," 
             << std::setw(16)<< std::fixed << std::setprecision(4) << m_pistonPosition << "," 
             << std::setw(16)<< std::fixed << std::setprecision(4) << efficiency << "," 
             << std::setw(16) << std::fixed << std::setprecision(4) << std::endl;
        file.flush();
        // clang-format on
    }
    file.close();
    std::cout << "弹头出膛!" << std::endl;
    result += outputData(step);
    result += computeEfficiency();

    return result;
}

// void InternalBallisticsSimulator::runSimulation(int numSteps) {
//     initialize();
//     for (int step = 0; step < numSteps; ++step) {
//         // Perform simulation step using Euler method (for simplicity)
//         m_position_ += m_velocity_ * m_timeStep_;
//         m_velocity_ += (force() / m_mass_) * m_timeStep_;

//         // Output data at regular intervals
//         if (step % static_cast<int>(m_outputInterval_ / m_timeStep_) == 0) {
//             outputData();
//         }
//     }
// }

// void InternalBallisticsSimulator::outputData() {
//     // Output data to console (you can modify this to write to a file or
//     other
//     // output)
//     std::cout << "Time: " << m_timeStep_ << ", Position: " << m_position_ <<
//     ", Velocity: " << m_velocity_ << std::endl;
// }

// double InternalBallisticsSimulator::force() {
//     // Implement your force calculation logic here (e.g., gravity, friction,
//     // etc.) For simplicity, returning a constant force in this example.
//     return -9.8;
// }
